package com.mall.common.exception;

import com.mall.common.entity.ResponseData;
import com.mall.common.enums.ResponseEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author wuyijie
 * @className: GlobalExceptionHandler
 * @description: TODO 全局异常处理
 * @date 2021/6/12 10:02
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * TODO: 自定义业务异常
     *
     * @param bizException
     * @return com.mall.commonservice.model.response.ResponseData
     * @author wuyijie
     * @date 2021/6/12 11:17
     */
    @ExceptionHandler(BizException.class)
    public ResponseData globalException(BizException bizException) {
        log.error("发生业务异常！原因:", bizException);

        return ResponseData.error(bizException.getErrorCode(), bizException.getErrorMessage());
    }

    /**
     * TODO: * 对方法参数校验异常处理方法(前端提交的方式为json格式出现异常时会被该异常类处理)
     * * json格式提交时，spring会采用json数据的数据转换器进行处理（进行参数校验时错误是抛出MethodArgumentNotValidException异常）
     *
     * @param e
     * @return com.mall.commonservice.model.response.ResponseData
     * @author wuyijie
     * @date 2021/6/12 10:58
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public ResponseData globalException(MethodArgumentNotValidException e) {
        // 获取所有异常
        List<String> errors = e.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(x -> x.getDefaultMessage())
                .collect(Collectors.toList());
        log.error("发生参数校验异常，异常为：", e);
        return ResponseData.error(String.join(",", errors));
    }

    /**
     * TODO: 处理所有参数校验时抛出的异常
     *
     * @param ex
     * @return com.mall.commonservice.model.response.ResponseData
     * @author wuyijie
     * @date 2021/6/12 11:17
     */
    @ExceptionHandler(value = ValidationException.class)
    public ResponseData handleBindException(ValidationException ex) {
        // 获取所有异常
        List<String> errors = new LinkedList<String>();
        if (ex instanceof ConstraintViolationException) {
            ConstraintViolationException exs = (ConstraintViolationException) ex;
            Set<ConstraintViolation<?>> violations = exs.getConstraintViolations();
            for (ConstraintViolation<?> item : violations) {
                errors.add(item.getMessage());
            }
        }
        log.error("发生参数校验异常，异常为：", ex);

        return ResponseData.error(String.join(",", errors));
    }

    /**
     * TODO: controller参数绑定异常
     *
     * @param e
     * @return com.mall.commonservice.model.response.ResponseData
     * @author wuyijie
     * @date 2021/6/12 11:01
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ResponseData globalException(MissingServletRequestParameterException e) {
        log.error("发生参数校验异常，异常为：", e);
        return ResponseData.error(e.getMessage());
    }

    /**
     * TODO: 对方法参数校验异常处理方法(仅对于表单提交有效，对于以json格式提交将会失效)
     * 如果是表单类型的提交，则spring会采用表单数据的处理类进行处理（进行参数校验错误时会抛出BindException异常）
     *
     * @param e
     * @return com.commonservice.model.response.ResponseData
     * @author wuyijie
     * @date 2021/6/13 20:45
     */
    @ExceptionHandler(BindException.class)
    public ResponseData globalException(BindException e) {
        log.error("发生参数校验异常，异常为：", e);
        return ResponseData.error(e.getMessage());
    }

    @ExceptionHandler(Exception.class)
    public ResponseData globalException(Exception e) {
        log.error("发生未知异常！原因：", e);
        return ResponseData.error(ResponseEnum.INTERNAL_SERVER_ERROR);
    }
}
